#pragma once
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <vector>
#include <ctime>
#include <semaphore.h>
using namespace std;

const static int defaultcap = 5;
pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
template <class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }

    void V(sem_t &sem)
    {
        sem_post(&sem);
    }

    void lock(pthread_mutex_t& mutex){
        pthread_mutex_lock(&mutex);
    }

    void unlock(pthread_mutex_t& mutex){
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int capacity = defaultcap)
        : _ringqueue(capacity),
          _capacity(capacity),
          _ci(0),
          _pi(0)
    {
        sem_init(&_p_spacesem, 0, _capacity);
        sem_init(&_c_datasem, 0, 0);
        pthread_mutex_init(&_p_mutex,nullptr);
        pthread_mutex_init(&_c_mutex,nullptr);
    }

    void push(T &in) // 生产
    {
        //先加锁再申请信号量的问题：这种做法会导致线程在信号量资源不足（如队列满）时阻塞，且阻塞期间仍持有锁，
        //使其他线程因无法获取锁而陷入等待，甚至引发整个生产流程卡死。同时，信号量的P操作本身是原子的，无需锁
        //保护，加锁会将其串行化，违背并发设计初衷，还会无意义地延长锁的持有时间，大幅降低效率。
        /*lock(_p_mutex);
        P(_p_spacesem);*/
        
        //先申请信号量，再加锁的好处：信号量（ _p_spacesem ）负责判断“是否有空闲空间”，锁（ _p_mutex ）负责
        //保护“修改队列和下标”的临界区。这种方式实现了信号量（资源控制）与锁（临界区保护）的职责分离，避免了锁
        //阻塞问题，且仅在必要时持有锁，能最大化并发效率，符合环形队列生产消费模型的逻辑。
        P(_p_spacesem);
        lock(_p_mutex);
        _ringqueue[_pi] = in;
        ++_pi;
        _pi %= _capacity;
        unlock(_p_mutex);
        V(_c_datasem);
    }

    void pop(T *out) // 消费
    {
        P(_c_datasem);
        lock(_c_mutex);
        *out = _ringqueue[_ci];
        ++_ci;
        _ci %= _capacity;
        unlock(_c_mutex);
        V(_p_spacesem);
    }

    ~RingQueue()
    {
        sem_destroy(&_p_spacesem);
        sem_destroy(&_c_datasem);
        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
    }

private:
    vector<T> _ringqueue;
    int _capacity;
    int _ci;
    int _pi;
    sem_t _p_spacesem;
    sem_t _c_datasem;

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};